<template>
  <el-dialog
    :title="`图片压缩工具`"
    v-model="drawerVisible"
    :draggable="true"
    :overflow="true"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    top="30px"
    width="70%"
    :destroy-on-close="true"
  >
    <el-form ref="ruleFormRef" style="overflow-y: auto" label-width="120px" label-position="top">
      <div class="detail-form">
        <!-- 上传区域 -->
        <div class="upload-section">
          <el-upload
            class="upload-demo"
            drag
            :auto-upload="false"
            :show-file-list="false"
            accept="image/*"
            :on-change="handleFileChange"
          >
            <el-icon class="el-icon--upload"><upload-filled /></el-icon>
            <div class="el-upload__text">将图片拖拽到此处，或<em>点击选择图片</em></div>
            <template #tip>
              <div class="el-upload__tip">支持 JPG、PNG、WebP 格式，文件大小不超过 10MB</div>
            </template>
          </el-upload>
        </div>

        <!-- 压缩设置 -->
        <div class="compress-settings" v-if="originalImage.file">
          <el-row :gutter="16">
            <el-col :span="24">
              <el-form-item label="压缩质量">
                <el-slider
                  v-model="compressOptions.quality"
                  :min="10"
                  :max="100"
                  :step="5"
                  show-tooltip
                  :format-tooltip="val => `${val}%`"
                  @change="handleQualityChange"
                />
                <div class="quality-presets">
                  <el-button size="small" @click="setQuality(25)">高压缩</el-button>
                  <el-button size="small" @click="setQuality(50)">均衡</el-button>
                  <el-button size="small" @click="setQuality(75)">高质量</el-button>
                  <el-button size="small" @click="setQuality(90)">最佳质量</el-button>
                </div>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="16">
            <el-col :span="12">
              <el-form-item label="输出格式">
                <el-select v-model="compressOptions.format" @change="handleFormatChange" style="width: 100%">
                  <el-option label="JPEG" value="image/jpeg" />
                  <el-option label="PNG" value="image/png" />
                  <el-option label="WebP" value="image/webp" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="尺寸缩放">
                <el-input-number
                  v-model="compressOptions.scale"
                  :min="0.1"
                  :max="2"
                  :step="0.1"
                  :precision="1"
                  @change="handleScaleChange"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
          </el-row>
        </div>

        <!-- 图片预览和对比 -->
        <div class="image-preview" v-if="originalImage.file">
          <el-row :gutter="16">
            <!-- 原始图片 -->
            <el-col :span="12">
              <div class="preview-card">
                <div class="preview-header">
                  <h4>原始图片</h4>
                  <el-tag type="info">{{ formatFileSize(originalImage.size) }}</el-tag>
                </div>
                <div class="preview-content">
                  <img :src="originalImage.preview" alt="原始图片" />
                </div>
                <div class="preview-info">
                  <p>尺寸: {{ originalImage.width }} × {{ originalImage.height }}</p>
                  <p>格式: {{ originalImage.type }}</p>
                </div>
              </div>
            </el-col>

            <!-- 压缩后图片 -->
            <el-col :span="12">
              <div class="preview-card">
                <div class="preview-header">
                  <h4>压缩后图片</h4>
                  <el-tag :type="compressionRatio > 50 ? 'success' : compressionRatio > 20 ? 'warning' : 'danger'">
                    {{ formatFileSize(compressedImage.size) }}
                  </el-tag>
                </div>
                <div class="preview-content">
                  <img v-if="compressedImage.preview" :src="compressedImage.preview" alt="压缩后图片" />
                  <div v-else class="preview-placeholder">
                    <el-icon><Picture /></el-icon>
                    <p>调整参数后预览压缩效果</p>
                  </div>
                </div>
                <div class="preview-info" v-if="compressedImage.size">
                  <p>尺寸: {{ compressedImage.width }} × {{ compressedImage.height }}</p>
                  <p>格式: {{ compressedImage.type }}</p>
                  <p class="compression-info">
                    <span :class="compressionRatio > 0 ? 'success' : 'error'"> 压缩率: {{ compressionRatio }}% </span>
                  </p>
                </div>
              </div>
            </el-col>
          </el-row>
        </div>

        <!-- 操作按钮 -->
        <div class="action-buttons" v-if="originalImage.file">
          <el-button type="primary" @click="compressImage" :loading="compressing">
            <el-icon><Refresh /></el-icon>
            重新压缩
          </el-button>
          <el-button type="success" @click="downloadImage" :disabled="!compressedImage.blob">
            <el-icon><Download /></el-icon>
            下载压缩图片
          </el-button>
          <el-button @click="resetAll">
            <el-icon><Delete /></el-icon>
            重新选择
          </el-button>
        </div>
      </div>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="drawerVisible = false">关闭</el-button>
      </div>
    </template>
  </el-dialog>
</template>

<script setup lang="ts" name="ImageCompress">
import { ref, computed, reactive } from "vue";
import { ElMessage } from "element-plus";
import { UploadFilled, Picture, Refresh, Download, Delete } from "@element-plus/icons-vue";
import type { UploadFile } from "element-plus";

interface DrawerProps {}

interface ImageInfo {
  file: File | null;
  preview: string;
  width: number;
  height: number;
  size: number;
  type: string;
}

interface CompressedImageInfo extends ImageInfo {
  blob: Blob | null;
}

interface CompressOptions {
  quality: number;
  format: string;
  scale: number;
}

const drawerVisible = ref(false);
const drawerProps = ref<DrawerProps>({});
const compressing = ref(false);

// 原始图片信息
const originalImage = reactive<ImageInfo>({
  file: null,
  preview: "",
  width: 0,
  height: 0,
  size: 0,
  type: ""
});

// 压缩后图片信息
const compressedImage = reactive<CompressedImageInfo>({
  file: null,
  preview: "",
  width: 0,
  height: 0,
  size: 0,
  type: "",
  blob: null
});

// 压缩选项
const compressOptions = reactive<CompressOptions>({
  quality: 75,
  format: "image/jpeg",
  scale: 1.0
});

// 计算压缩率
const compressionRatio = computed(() => {
  if (!originalImage.size || !compressedImage.size) return 0;
  return Math.round(((originalImage.size - compressedImage.size) / originalImage.size) * 100);
});

// 接收父组件传过来的参数
const acceptParams = async (params: DrawerProps) => {
  drawerProps.value = params;
  drawerVisible.value = true;
  resetAll();
};

// 处理文件选择
const handleFileChange = (uploadFile: UploadFile) => {
  const file = uploadFile.raw;
  if (!file) return;

  // 验证文件类型
  if (!file.type.startsWith("image/")) {
    ElMessage.error("请选择图片文件");
    return;
  }

  // 验证文件大小（10MB）
  if (file.size > 10 * 1024 * 1024) {
    ElMessage.error("图片文件大小不能超过 10MB");
    return;
  }

  loadImageInfo(file);
};

// 加载图片信息
const loadImageInfo = (file: File) => {
  const reader = new FileReader();
  reader.onload = e => {
    const img = new Image();
    img.onload = () => {
      // 设置原始图片信息
      originalImage.file = file;
      originalImage.preview = e.target?.result as string;
      originalImage.width = img.width;
      originalImage.height = img.height;
      originalImage.size = file.size;
      originalImage.type = file.type;

      // 根据原始格式设置默认输出格式
      if (file.type === "image/png") {
        compressOptions.format = "image/png";
      } else if (file.type === "image/webp") {
        compressOptions.format = "image/webp";
      } else {
        compressOptions.format = "image/jpeg";
      }

      // 自动进行首次压缩
      compressImage();
    };
    img.src = e.target?.result as string;
  };
  reader.readAsDataURL(file);
};

// 压缩图片
const compressImage = async () => {
  if (!originalImage.file) return;

  compressing.value = true;
  try {
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    const img = new Image();

    img.onload = () => {
      // 计算新尺寸
      const newWidth = Math.round(originalImage.width * compressOptions.scale);
      const newHeight = Math.round(originalImage.height * compressOptions.scale);

      canvas.width = newWidth;
      canvas.height = newHeight;

      // 绘制图片
      ctx?.drawImage(img, 0, 0, newWidth, newHeight);

      // 转换为 Blob
      canvas.toBlob(
        blob => {
          if (blob) {
            // 设置压缩后图片信息
            compressedImage.blob = blob;
            compressedImage.preview = canvas.toDataURL(compressOptions.format, compressOptions.quality / 100);
            compressedImage.width = newWidth;
            compressedImage.height = newHeight;
            compressedImage.size = blob.size;
            compressedImage.type = compressOptions.format;
          }
          compressing.value = false;
        },
        compressOptions.format,
        compressOptions.quality / 100
      );
    };

    img.src = originalImage.preview;
  } catch (error) {
    ElMessage.error("图片压缩失败");
    compressing.value = false;
  }
};

// 设置质量预设
const setQuality = (quality: number) => {
  compressOptions.quality = quality;
  compressImage();
};

// 质量变化处理
const handleQualityChange = () => {
  compressImage();
};

// 格式变化处理
const handleFormatChange = () => {
  compressImage();
};

// 缩放变化处理
const handleScaleChange = () => {
  compressImage();
};

// 下载压缩图片
const downloadImage = () => {
  if (!compressedImage.blob || !originalImage.file) return;

  const url = URL.createObjectURL(compressedImage.blob);
  const link = document.createElement("a");
  link.href = url;

  // 生成文件名
  const originalName = originalImage.file.name;
  const nameWithoutExt = originalName.substring(0, originalName.lastIndexOf("."));
  const extension = compressOptions.format.split("/")[1];
  link.download = `${nameWithoutExt}_compressed.${extension}`;

  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  URL.revokeObjectURL(url);

  ElMessage.success("图片下载成功");
};

// 重置所有数据
const resetAll = () => {
  originalImage.file = null;
  originalImage.preview = "";
  originalImage.width = 0;
  originalImage.height = 0;
  originalImage.size = 0;
  originalImage.type = "";

  compressedImage.file = null;
  compressedImage.preview = "";
  compressedImage.width = 0;
  compressedImage.height = 0;
  compressedImage.size = 0;
  compressedImage.type = "";
  compressedImage.blob = null;

  compressOptions.quality = 75;
  compressOptions.format = "image/jpeg";
  compressOptions.scale = 1.0;
};

// 格式化文件大小
const formatFileSize = (bytes: number): string => {
  if (bytes === 0) return "0 B";
  const k = 1024;
  const sizes = ["B", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return (bytes / Math.pow(k, i)).toFixed(2) + " " + sizes[i];
};

defineExpose({
  acceptParams
});
</script>

<style lang="scss" scoped>
.detail-form {
  flex: 1 1 200px;
  overflow-y: auto;
}
.upload-section {
  margin-bottom: 16px;
  .upload-demo {
    width: 100%;
  }
}
.compress-settings {
  padding: 16px;
  margin-bottom: 16px;
  background: var(--el-bg-color-page);
  border-radius: 8px;
  .quality-presets {
    display: flex;
    gap: 8px;
    margin-top: 8px;
  }
}
.image-preview {
  margin-bottom: 16px;
}
.preview-card {
  overflow: hidden;
  border: 1px solid var(--el-border-color);
  border-radius: 8px;
  .preview-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 12px;
    background: var(--el-bg-color-page);
    border-bottom: 1px solid var(--el-border-color);
    h4 {
      margin: 0;
      font-size: 14px;
      font-weight: 600;
    }
  }
  .preview-content {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 120px;
    padding: 8px;
    text-align: center;
    img {
      max-width: 100%;
      max-height: 160px;
      border-radius: 4px;
      box-shadow: 0 2px 8px rgb(0 0 0 / 10%);
    }
    .preview-placeholder {
      color: var(--el-text-color-placeholder);
      .el-icon {
        margin-bottom: 8px;
        font-size: 36px;
      }
    }
  }
  .preview-info {
    padding: 8px 12px;
    font-size: 12px;
    color: var(--el-text-color-regular);
    background: var(--el-bg-color-page);
    border-top: 1px solid var(--el-border-color);
    p {
      margin: 2px 0;
    }
    .compression-info {
      .success {
        font-weight: 600;
        color: var(--el-color-success);
      }
      .error {
        font-weight: 600;
        color: var(--el-color-danger);
      }
    }
  }
}
.action-buttons {
  padding: 16px;
  text-align: center;
  border-top: 1px solid var(--el-border-color);
  .el-button {
    margin: 0 6px;
  }
}

// 响应式设计
@media (width <= 768px) {
  .image-preview {
    .el-row {
      .el-col {
        margin-bottom: 12px;
      }
    }
  }
  .action-buttons {
    .el-button {
      display: block;
      width: 100%;
      margin: 6px 0;
    }
  }
}
</style>
